package learn;

import java.util.*;

/**
 * 定时器
 * 设置个时间，在时间到了之后执行指定的逻辑
 */
public class timer {
    public static void main(String[] args) {
        //这是已经封装好了的，直接就可以使用
        //解决了 java中实现的计时器在虽然时将TimerTask和time分开传入的，但是其实还是将time赋值给了nextExecutionTime这个变量
        //对于TimerThread这个类的run调用其实时在实例化Timer这个对象时就已经调用了
        //TimerThread继承了Thread这个类，在实例化时会调用thread.start()这个方法，然后就会调用run方法
        //这是就会使run方法死循环运行起来以处理任务

        Timer timer = new Timer("hadage");
        Scanner s = new Scanner(System.in);
        s.next();
        TimerTask t1 = new TimerTask() {//要定时的任务 重写run
            @Override
            public void run() {
                for (int i = 0; i < 1; i++) {
                    System.out.print("执行t1 ");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                }
            }
        };
        //在指定时间过后就执行执行指定的任务 period表示该任务会以设置的时间间隔来重复执行
        // 若为0表示不是重复任务，若为正表示该任务会以一个固定的时间间隔去执行，若在设定的时间周期内完成该任务
        // 就会开辟新的线程去继续执新的该任务
        // 若为负数就是基于该任务结束后的时间来确认下一次执行该任务的时间
        timer.schedule(t1, 1110,1000);
        System.out.println("end main");
    }
}

/**
 * 模拟实现
 * 建立小根堆以时间为比较，将到了时间的任务出出去执行，若还没到
 * 就让该线程处于阻塞状态

 * 实现schedule
 * 使用MyTask 将要执行的任务和执行的时间组织起来，要执行的任务通过重写Runnable接口的run方法来实现
 * MyTask这个方法实际上是只能MyTimer来调用的，向
 */
class MyTimer{
    //任务
   private class MyTask implements Comparable<MyTask>{
        long time = 0;
        Runnable run;//要执行的方法

        public MyTask(Runnable run,long time) {
            this.time = time;
            this.run = run;
        }

        @Override
        public int compareTo(MyTask o) {
            return (int) (this.time - o.time);//若返回的是-1就会交换
//        return (int) (o.time - this.time);
        }
    }
    final static Object locker = new Object();
    PriorityQueue<MyTask> taskQueue = new PriorityQueue();//他在比较是会使用类中的compare方法

    public MyTimer() {
        //创建一个线程，这个线程在MyTimer出创建了就会一直运行，用来处理传入的任务
        Thread thread = new Thread(() -> {
            //创建一个线程去执行run 会在创建是死循环的去判定
            while (true) {
                synchronized (locker) {
                    while(taskQueue.isEmpty()) {//使用if的话如果队列为空那么一直运行这段代码
                        //改为while确保出这个循环之后一定不为空
                        //但是却什么都没有做，这就造成了忙等的现象这是不科学的，按理说应该进入等待状态
                        //把资源空出来给别人用
//                        continue;
                        try {//解决忙等
                            locker.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    MyTask m = taskQueue.peek();
                    if (m.time > System.currentTimeMillis()) {//说明时间还没有到
//                        continue;//等待也不应该这么快的就去确认时间是否到了，也是使用wait来降低询问的次数
                        try {
                            locker.wait(m.time - System.currentTimeMillis());
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    } else {//执行方法
                        m.run.run();
                        taskQueue.poll();
                    }
                }
            }
        });
        thread.start();
    }

    public void schedule(Runnable task, long delay) throws InterruptedException {

        synchronized (locker) {//在java中也强制要求notify要搭配锁使用
        //用这个类来储存时间和要执行的方法
        MyTask myTask = new MyTask(task, System.currentTimeMillis() + delay);
//        Thread.sleep(1000);
            taskQueue.offer(myTask);
            locker.notify();//队列不为空 唤醒wait
        }
    }
    /*
    public static void main(String[] args) throws InterruptedException {
        MyTimer m = new MyTimer();
        Scanner s = new Scanner(System.in);
        s.next();
        m.schedule(() -> {
            System.out.println("eee");
        }, 1000);
    }
    */
}

class textRun extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("自己调用的");
        }

    }
}
class use{
    private textRun t = new textRun();

    public void rr() {

    }
}
class   mainRR {
    public static void main(String[] args) {
        textRun t = new textRun();
        use u = new use();
    }
}


















